Reactning useFormState hooki yordamida server tomoni validatsiyasi va holatni boshqarishni oʻrganing. Amaliy misollar bilan mustahkam, progressiv takomillashtirilgan formalar yarating.
React useFormState: Zamonaviy Formalar Holatini Boshqarish va Validatsiyaga Chuqur Sho'ng'ish
Formalar veb-interaktivlikning asosidir. Oddiy aloqa formalaridan tortib murakkab ko'p bosqichli sehrgarlargacha, ular foydalanuvchi kiritishi va ma'lumotlarni yuborish uchun zarurdir. Ko'p yillar davomida React dasturchilari holatni boshqarish strategiyalari landshaftida harakat qilishdi, useState bilan boshqariladigan komponentlarni qo'lda ishlashdan tortib, Formik va React Hook Form kabi kuchli uchinchi tomon kutubxonalaridan foydalanishgacha. Ushbu yechimlar a'lo darajada bo'lsa-da, React asosiy jamoasi formalar va server o'rtasidagi aloqani qayta ko'rib chiqadigan yangi, kuchli primitivni taqdim etdi: useFormState hooki.
React Server Actions bilan birga taqdim etilgan bu hook shunchaki navbatdagi holatni boshqarish vositasi emas. Bu mustahkamlik, foydalanuvchi tajribasi va tez-tez gapiriladigan, lekin amalga oshirish qiyin bo'lgan konsepsiyani birinchi o'ringa qo'yadigan yanada integratsiyalashgan, serverga yo'naltirilgan arxitekturaning asosiy qismidir: progressiv takomillashtirish.
Ushbu to'liq qo'llanmada biz useFormStatening har bir jihatini o'rganamiz. Biz asoslardan boshlaymiz, uni an'anaviy usullar bilan taqqoslaymiz, amaliy misollar yaratamiz va validatsiya hamda foydalanuvchi fikr-mulohazalari uchun ilg'or na'munalarga sho'ng'iymiz. Oxirida siz nafaqat bu hookdan qanday foydalanishni, balki zamonaviy React ilovalarida formalar yaratish uchun u ifodalaydigan paradigma o'zgarishini ham tushunasiz.
`useFormState` nima va u nima uchun muhim?
Aslida, useFormState bu forma harakati natijasiga asoslangan holda forma holatini boshqarish uchun mo'ljallangan React Hookidir. Bu oddiy eshitilishi mumkin, lekin uning haqiqiy kuchi mijoz tomonidagi yangilanishlarni server tomonidagi mantiq bilan uzluksiz birlashtiradigan dizaynida yotadi.
Oddiy forma yuborish jarayonini o'ylab ko'ring:
- Foydalanuvchi formani to'ldiradi.
- Foydalanuvchi "Yuborish" tugmasini bosadi.
- Mijoz ma'lumotlarni server API endpointiga yuboradi.
- Server ma'lumotlarni qayta ishlaydi, uni tasdiqlaydi va biror harakatni bajaradi (masalan, ma'lumotlar bazasiga saqlaydi).
- Server javob qaytaradi (masalan, muvaffaqiyat xabari yoki validatsiya xatolari ro'yxati).
- Mijoz tomonidagi kod bu javobni tahlil qilishi va UI'ni mos ravishda yangilashi kerak.
An'anaga ko'ra, bu yuklanish holatlari, xatolik holatlari va muvaffaqiyat holatlarini qo'lda boshqarishni talab qilar edi. useFormState bu butun jarayonni soddalashtiradi, ayniqsa Next.js kabi freymvorklarda Server Actions bilan ishlatilganda. U formaning yuborilishi va u yaratadigan holat o'rtasida to'g'ridan-to'g'ri, deklarativ bog'liqlikni yaratadi.
Eng muhim afzallik - bu progressiv takomillashtirishdir. useFormState va server harakati bilan qurilgan forma JavaScript o'chirilgan bo'lsa ham mukammal ishlaydi. Brauzer to'liq sahifani yuborishni amalga oshiradi, server harakati ishga tushadi va server keyingi sahifani natijaviy holat bilan render qiladi (masalan, ko'rsatilgan validatsiya xatolari bilan). JavaScript yoqilganda, React boshqaruvni o'z qo'liga oladi, to'liq sahifani qayta yuklashni oldini oladi va silliq, bir sahifali ilova (SPA) tajribasini taqdim etadi. Siz bitta kod bazasi bilan ikkala dunyoning eng yaxshi tomonlariga ega bo'lasiz.
Asoslarni Tushunish: `useFormState` vs. `useState`
`useFormState`ni tushunish uchun uni tanish `useState` hooki bilan taqqoslash foydalidir. Ikkalasi ham holatni boshqarsa-da, ularning yangilanish mexanizmlari va asosiy foydalanish holatlari farq qiladi.
`useFormState` ning imzosi quyidagicha:
const [state, formAction] = useFormState(fn, initialState);
Imzoni Tahlil Qilish:
fn: Forma yuborilganda chaqiriladigan funksiya. Bu odatda server harakatidir. U ikkita argumentni qabul qiladi: oldingi holat va forma ma'lumotlari. Uning qaytaradigan qiymati yangi holatga aylanadi.initialState: Forma hech qachon yuborilishidan oldin holatning boshlang'ich qiymati.state: Formaning joriy holati. Dastlabki renderda uinitialStatega teng. Forma yuborilgandan so'ng, u sizning harakat funksiyangizfnning qaytarilgan qiymatiga aylanadi.formAction: Sizning<form>elementingizningactionpropiga uzatadigan yangi harakat. Ushbu harakat chaqirilganda (forma yuborilganda), u sizning asl funksiyangizfnni chaqiradi va holatni yangilaydi.
Konseptual Taqqoslash
Oddiy hisoblagichni tasavvur qiling.
useState bilan yangilanishni o'zingiz boshqarasiz:
const [count, setCount] = useState(0);
function handleIncrement() {
setCount(c => c + 1);
}
Bu yerda, handleIncrement holatni o'rnatuvchini aniq chaqiradigan hodisa ishlovchisidir.
useFormState bilan holatning yangilanishi harakat natijasidir:
// Bu tasvirlash uchun soddalashtirilgan, server harakati bo'lmagan misol
function incrementAction(previousState, formData) {
// Agar bu haqiqiy forma bo'lsa, formData yuborish ma'lumotlarini o'z ichiga olgan bo'lardi
return previousState + 1;
}
const [count, dispatchIncrement] = useFormState(incrementAction, 0);
// Siz `dispatchIncrement` ni formaning action propida ishlatgan bo'lardingiz.
Asosiy farq shundaki, useFormState asinxron, natijaga asoslangan holatni yangilash oqimi uchun mo'ljallangan, bu serverga forma yuborish paytida aynan sodir bo'ladigan jarayondir. Siz `setState` funksiyasini chaqirmaysiz; siz harakatni jo'natasiz va hook holatni harakatning qaytarilgan qiymati bilan yangilaydi.
Amaliy Qo'llash: `useFormState` Bilan Birinchi Formangizni Yaratish
Keling, nazariyadan amaliyotga o'tamiz. Biz useFormState ning asosiy funksionalligini namoyish etadigan oddiy yangiliklarga obuna bo'lish formasini yaratamiz. Ushbu misol React Server Actions-ni qo'llab-quvvatlaydigan freymvork, masalan, Next.js bilan App Router, sozlamasini nazarda tutadi.
1-qadam: Server Harakatini Aniqlash
Server harakati bu siz 'use server'; direktivasi bilan belgilashingiz mumkin bo'lgan funksiyadir. Bu funksiyaning mijoz komponentidan chaqirilganda ham serverda xavfsiz tarzda bajarilishiga imkon beradi. Bu useFormState uchun mukammal sherikdir.
Keling, masalan, app/actions.js faylini yaratamiz:
'use server';
// Bu soddalashtirilgan harakat. Haqiqiy ilovada siz emailni tasdiqlab,
// uni ma'lumotlar bazasiga yoki uchinchi tomon xizmatiga saqlagan bo'lardingiz.
export async function subscribeToNewsletter(previousState, formData) {
const email = formData.get('email');
// Asosiy server tomonidagi validatsiya
if (!email || !email.includes('@')) {
return {
message: 'Iltimos, yaroqli elektron pochta manzilini kiriting.',
success: false
};
}
console.log(`Yangi obunachi: ${email}`);
// Ma'lumotlar bazasiga saqlashni simulyatsiya qilish
await new Promise(res => setTimeout(res, 1000));
return {
message: 'Obuna bo\'lganingiz uchun rahmat!',
success: true
};
}
Funksiya imzosiga e'tibor bering: (previousState, formData). Bu useFormState bilan ishlatiladigan funksiyalar uchun talab qilinadi. Biz emailni tekshiramiz va komponentimizning yangi holatiga aylanadigan tuzilgan obyektni qaytaramiz.
2-qadam: Forma Komponentini Yaratish
Endi, keling, ushbu harakatdan foydalanadigan mijoz tomonidagi komponentni yaratamiz.
'use client';
import { useFormState } from 'react-dom';
import { subscribeToNewsletter } from './actions';
const initialState = {
message: null,
success: false,
};
export function NewsletterForm() {
const [state, formAction] = useFormState(subscribeToNewsletter, initialState);
return (
<div>
<h3>Yangiliklarimizga Obuna Bo'ling</h3>
<form action={formAction}>
<label htmlFor="email">Elektron pochta manzili:</label>
<input type="email" id="email" name="email" required />
<button type="submit">Obuna bo'lish</button>
</form>
{state.message && (
<p style={{ color: state.success ? 'green' : 'red' }}>
{state.message}
</p>
)}
</div>
);
}
Komponentni Tahlil Qilish:
- Biz
useFormStatenireact-domdan import qilamiz. Bu muhim — u asosiyreactpaketida emas. - Biz
initialStateobyektini aniqlaymiz. Bu bizningstateo'zgaruvchimizning birinchi renderda yaxshi aniqlangan bo'lishini ta'minlaydi. - Biz
stateva o'ralganformActionni olish uchunuseFormState(subscribeToNewsletter, initialState)ni chaqiramiz. - Biz ushbu
formActionni to'g'ridan-to'g'ri<form>elementiningactionpropiga uzatamiz. Bu sehrli bog'lanishdir. - Biz
state.messagega asoslanib, shartli ravishda xabar chiqaramiz va uni muvaffaqiyat va xato holatlari uchun har xil uslubda bezatamiz.
Endi, foydalanuvchi formani yuborganda, quyidagilar sodir bo'ladi:
- React yuborishni to'xtatib qoladi.
- U joriy holat va forma ma'lumotlari bilan
subscribeToNewsletterserver harakatini chaqiradi. - Server harakati ishlaydi, o'z mantig'ini bajaradi va yangi holat obyektini qaytaradi.
useFormStatebu yangi obyektni qabul qiladi vaNewsletterFormkomponentining yangilanganstatebilan qayta render qilinishini ishga tushiradi.- Muvaffaqiyat yoki xato xabari forma ostida, to'liq sahifani qayta yuklamasdan paydo bo'ladi.
`useFormState` Bilan Ilg'or Forma Validatsiyasi
Oldingi misol oddiy xabarni ko'rsatdi. useFormState ning haqiqiy kuchi serverdan qaytarilgan murakkab, maydonga xos validatsiya xatolarini qayta ishlashda namoyon bo'ladi.
1-qadam: Batafsil Xatolar Uchun Server Harakatini Kengaytirish
Keling, yanada mustahkamroq ro'yxatdan o'tish formasi harakatini yaratamiz. U foydalanuvchi nomi, elektron pochta va parolni tasdiqlaydi, kalitlari maydon nomlariga mos keladigan xatolar obyektini qaytaradi.
app/actions.js da:
'use server';
export async function registerUser(previousState, formData) {
const username = formData.get('username');
const email = formData.get('email');
const password = formData.get('password');
const errors = {};
if (!username || username.length < 3) {
errors.username = 'Foydalanuvchi nomi kamida 3 belgidan iborat bo\'lishi kerak.';
}
if (!email || !email.includes('@')) {
errors.email = 'Iltimos, yaroqli elektron pochta manzilini kiriting.';
} else if (await isEmailTaken(email)) { // Ma'lumotlar bazasini tekshirishni simulyatsiya qilish
errors.email = 'Ushbu elektron pochta allaqachon ro\'yxatdan o\'tgan.';
}
if (!password || password.length < 8) {
errors.password = 'Parol kamida 8 belgidan iborat bo\'lishi kerak.';
}
if (Object.keys(errors).length > 0) {
return { errors };
}
// Foydalanuvchini ro'yxatdan o'tkazishni davom ettirish...
console.log('Foydalanuvchini ro\'yxatdan o\'tkazish:', { username, email });
return { message: 'Ro\'yxatdan o\'tish muvaffaqiyatli! Tasdiqlash uchun elektron pochtangizni tekshiring.' };
}
// Ma'lumotlar bazasini qidirishni simulyatsiya qilish uchun yordamchi funksiya
async function isEmailTaken(email) {
if (email === 'test@example.com') {
return true;
}
return false;
}
Endi bizning harakatimiz ikki xil shakldagi holat obyektini qaytarishi mumkin: { errors: { ... } } yoki { message: '...' }.
2-qadam: Maydonga Xos Xatolarni Ko'rsatish Uchun Forma Yaratish
Endi mijoz komponenti ushbu tuzilgan xato obyektini o'qishi va tegishli kiritish maydonlari yonida xabarlarni ko'rsatishi kerak.
'use client';
import { useFormState } from 'react-dom';
import { registerUser } from './actions';
const initialState = {
message: null,
errors: {},
};
export function RegistrationForm() {
const [state, formAction] = useFormState(registerUser, initialState);
return (
<form action={formAction}>
<h2>Hisob Yaratish</h2>
{state?.message && <p className="success-message">{state.message}</p>}
<div className="form-group">
<label htmlFor="username">Foydalanuvchi nomi</label>
<input id="username" name="username" aria-describedby="username-error" />
{state?.errors?.username && (
<p id="username-error" className="error-message">{state.errors.username}</p>
)}
</div>
<div className="form-group">
<label htmlFor="email">Elektron pochta</label>
<input id="email" name="email" type="email" aria-describedby="email-error" />
{state?.errors?.email && (
<p id="email-error" className="error-message">{state.errors.email}</p>
)}
</div>
<div className="form-group">
<label htmlFor="password">Parol</label>
<input id="password" name="password" type="password" aria-describedby="password-error" />
{state?.errors?.password && (
<p id="password-error" className="error-message">{state.errors.password}</p>
)}
</div>
<button type="submit">Ro'yxatdan o'tish</button>
</form>
);
}
Maxsus imkoniyatlar eslatmasi: Biz kiritish maydonida aria-describedby atributidan foydalanamiz, u xato xabari konteynerining ID-siga ishora qiladi. Bu ekran o'quvchi foydalanuvchilar uchun juda muhim, chunki u kiritish maydonini uning maxsus validatsiya xatosi bilan dasturiy ravishda bog'laydi.
Mijoz Tomonidagi Validatsiya Bilan Birlashtirish
Server tomonidagi validatsiya haqiqat manbaidir, ammo foydalanuvchiga elektron pochtasida '@' belgisini unutganligini aytish uchun serverga borib-kelishni kutish yomon tajribadir. useFormState mijoz tomonidagi validatsiyani almashtirmaydi; u uni mukammal to'ldiradi.
Siz tezkor fikr-mulohaza uchun standart HTML5 validatsiya atributlarini qo'shishingiz mumkin:
<input
id="username"
name="username"
required
minLength="3"
aria-describedby="username-error"
/>
<input
id="email"
name="email"
type="email"
required
aria-describedby="email-error"
/>
Buning yordamida brauzer ushbu asosiy mijoz tomoni qoidalari bajarilmagan bo'lsa, formani yuborishni oldini oladi. useFormState oqimi faqat mijoz tomonida yaroqli bo'lgan ma'lumotlar uchun ishga tushadi, u yerda u murakkabroq, xavfsiz server tomonidagi tekshiruvlarni (masalan, elektron pochtaning allaqachon ishlatilganligini) amalga oshiradi.
`useFormStatus` Bilan Kutish Holatidagi UI'ni Boshqarish
Forma yuborilganda, server harakati bajarilayotgan paytda kechikish bo'ladi. Yaxshi foydalanuvchi tajribasi bu vaqt davomida fikr-mulohaza berishni o'z ichiga oladi, masalan, yuborish tugmasini o'chirib qo'yish va yuklanish indikatorini ko'rsatish.
React aynan shu maqsadda hamroh hookni taqdim etadi: useFormStatus.
useFormStatus hooki oxirgi forma yuborilishi haqida holat ma'lumotlarini taqdim etadi. Muhimi, u holatini kuzatmoqchi bo'lgan <form> komponenti ichida render qilinishi kerak.
Aqlli Yuborish Tugmasini Yaratish
Ushbu hookdan foydalanadigan yuborish tugmasi uchun alohida komponent yaratish eng yaxshi amaliyotdir.
'use client';
import { useFormStatus } from 'react-dom';
export function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? 'Yuborilmoqda...' : 'Ro\'yxatdan o\'tish'}
</button>
);
}
Endi biz ushbu SubmitButton ni import qilib, RegistrationFormimizda ishlatishimiz mumkin:
// ... RegistrationForm komponenti ichida
import { SubmitButton } from './SubmitButton';
// ...
<SubmitButton />
</form>
// ...
Foydalanuvchi tugmani bosganda, quyidagilar sodir bo'ladi:
- Forma yuborilishi boshlanadi.
SubmitButtonichidagiuseFormStatushookipending: truedeb xabar beradi.SubmitButtonkomponenti qayta render qilinadi. Tugma o'chiriladi va uning matni "Yuborilmoqda..." ga o'zgaradi.- Server harakati tugagach va
useFormStateholatni yangilagach, forma endi kutish holatida bo'lmaydi. useFormStatuspending: falsedeb xabar beradi va tugma o'zining normal holatiga qaytadi.
Bu oddiy na'muna formaning holati haqida aniq, darhol fikr-mulohaza berish orqali foydalanuvchi tajribasini keskin yaxshilaydi.
Eng Yaxshi Amaliyotlar va Umumiy Xatolar
useFormStateni loyihalaringizga integratsiya qilayotganda, umumiy muammolardan qochish uchun ushbu ko'rsatmalarni yodda tuting.
Tavsiya qilinadi (Do's)
- Yaxshi aniqlangan
initialStatetaqdim eting. Bu dastlabki renderda holat xususiyatlaringiz (masalan,errors) aniqlanmagan bo'lishi mumkin bo'lgan xatolarni oldini oladi. - Holatingiz shaklini izchil saqlang. Har doim harakatingizdan bir xil kalitlarga ega bo'lgan obyektni qaytaring (masalan,
message,errors), hatto ularning qiymatlari null yoki bo'sh bo'lsa ham. Bu sizning mijoz tomonidagi renderlash mantig'ingizni soddalashtiradi. - UX fikr-mulohazasi uchun
useFormStatusdan foydalaning. Yuborish paytida o'chirilgan tugma professional foydalanuvchi tajribasi uchun muhokama qilinmaydi. - Maxsus imkoniyatlarga ustunlik bering.
labelteglaridan foydalaning va xato xabarlarini kiritish maydonlarigaaria-describedbybilan bog'lang. - Yangi holat obyektlarini qaytaring. Server harakatingizda har doim yangi obyekt qaytaring.
previousStateargumentini o'zgartirmang.
Tavsiya qilinmaydi (Don'ts)
- Birinchi argumentni unutmang. Sizning harakat funksiyangiz birinchi argument sifatida
previousStateni qabul qilishi kerak, hatto undan foydalanmasangiz ham. useFormStatusni<form>dan tashqarida chaqirmang. U ishlamaydi. U kuzatayotgan formaning avlodi bo'lishi kerak.- Mijoz tomonidagi validatsiyadan voz kechmang. Oddiy cheklovlar bo'yicha tezkor fikr-mulohaza uchun HTML5 atributlaridan yoki yengil kutubxonadan foydalaning. Biznes mantig'i va xavfsizlik validatsiyasi uchun serverga tayaning.
- Nozik mantig'ni forma komponentiga joylashtirmang. Ushbu na'munaning go'zalligi shundaki, sizning barcha muhim validatsiya va ma'lumotlarni qayta ishlash mantig'ingiz serverda, harakatda xavfsiz joylashgan.
`useFormState` ni Boshqa Kutubxonalar O'rniga Qachon Tanlash Kerak
React boy forma kutubxonalari ekotizimiga ega. Xo'sh, qachon o'rnatilgan useFormState ga murojaat qilish kerak va qachon React Hook Form yoki Formik kabi kutubxonaga?
`useFormState` ni tanlang, qachonki:
- Siz zamonaviy, serverga yo'naltirilgan freymvorkdan foydalanayotgan bo'lsangiz. U Next.js (App Router), Remix va boshqalar kabi freymvorklarda Server Actions bilan ishlash uchun mo'ljallangan.
- Progressiv takomillashtirish ustuvor bo'lsa. Agar formalaringiz JavaScriptsiz ishlashi kerak bo'lsa, bu eng yaxshi, o'rnatilgan yechimdir.
- Sizning validatsiyangiz ko'p jihatdan serverga bog'liq bo'lsa. Eng muhim validatsiya qoidalari ma'lumotlar bazasini qidirishni yoki murakkab biznes mantig'ini talab qiladigan formalar uchun
useFormStatetabiiy tanlovdir. - Siz mijoz tomonidagi JavaScriptni minimallashtirishni xohlasangiz. Ushbu na'muna holatni boshqarish va validatsiya mantig'ini serverga yuklaydi, bu esa yengilroq mijoz to'plamiga olib keladi.
Boshqa kutubxonalarni (masalan, React Hook Form) ko'rib chiqing, qachonki:
- Siz an'anaviy SPA (bir sahifali ilova) qurayotgan bo'lsangiz. Agar ilovangiz REST yoki GraphQL API'lari bilan aloqa qiladigan Mijoz Tomonida Renderlanadigan (CSR) ilova bo'lsa, maxsus mijoz tomoni kutubxonasi ko'pincha ergonomikroqdir.
- Sizga juda murakkab, faqat mijoz tomonidagi interaktivlik kerak bo'lsa. Murakkab real vaqtda validatsiya, umumiy mijoz holatiga ega ko'p bosqichli sehrgarlar, dinamik maydonlar massivlari yoki yuborishdan oldin murakkab ma'lumotlarni o'zgartirish kabi xususiyatlar uchun yetuk kutubxonalar ko'proq tayyor yordamchi dasturlarni taklif qiladi.
- Juda katta formalar uchun unumdorlik muhim bo'lsa. React Hook Form kabi kutubxonalar mijozdagi qayta renderlarni minimallashtirish uchun optimallashtirilgan, bu o'nlab yoki yuzlab maydonlarga ega formalar uchun foydali bo'lishi mumkin.
Tanlov bir-birini istisno qilmaydi. Katta ilovada siz oddiy serverga bog'liq formalar (masalan, aloqa yoki ro'yxatdan o'tish formalari) uchun useFormState dan va faqat mijoz tomonida interaktiv bo'lgan murakkab sozlamalar paneli uchun to'liq funksiyali kutubxonadan foydalanishingiz mumkin.
Xulosa: Reactdagi Formalarning Kelajagi
useFormState hooki shunchaki yangi API emas; bu Reactning rivojlanayotgan falsafasining aksidir. Forma holatini server tomonidagi harakatlar bilan chambarchas bog'lash orqali u mijoz-server bo'linishini ham kuchli, ham oddiy his etiladigan tarzda bartaraf etadi.
Ushbu hookdan foydalanib, siz uchta muhim afzallikka ega bo'lasiz:
- Soddalashtirilgan Holat Boshqaruvi: Siz ma'lumotlarni qo'lda olish, yuklanish holatlarini boshqarish va server javoblarini tahlil qilishning ortiqcha ishlaridan xalos bo'lasiz.
- Standart bo'yicha Mustahkamlik: Progressiv takomillashtirish ichiga o'rnatilgan bo'lib, formalaringiz qurilmasi yoki tarmoq sharoitlaridan qat'i nazar, barcha foydalanuvchilar uchun ochiq va funksional bo'lishini ta'minlaydi.
- Vazifalarning Aniq Ajratilishi: UI mantig'i mijoz komponentlaringizda qoladi, biznes va validatsiya mantig'i esa serverda xavfsiz tarzda bir joyda joylashadi.
React ekotizimi serverga yo'naltirilgan na'munalarni qabul qilishda davom etar ekan, useFormState va uning hamrohi useFormStatusni o'zlashtirish zamonaviy, chidamli va foydalanuvchiga qulay veb-ilovalar yaratmoqchi bo'lgan dasturchilar uchun muhim mahorat bo'ladi. Bu bizni vebni o'zi mo'ljallanganidek — chidamli va hamma uchun ochiq — qurishga undaydi, shu bilan birga foydalanuvchilar kutadigan boy, interaktiv tajribalarni taqdim etishda davom etadi.